<template>
  <div
    v-show="loaded"
    class="piano-bg"
    @mousedown="keyAreaMousedown"
    @mouseup="keyAreaMouseup"
  >
    <svg
      :width="whiteKeyWidth * 49"
      :height="whiteKeyHeight"
      @mousedown="mouseEvent"
      @mouseup="mouseEvent"
      @mouseout="mouseEvent"
      @mouseover="mouseEvent"
    >
      <g
        v-for="g of 7"
        :key="'g' + g"
        :transform="'translate(' + whiteKeyWidth * (g - 1) * 7 + ', 0)'"
      >
        <g
          v-for="i in whiteKeyPositions"
          :key="'w' + i"
          :transform="'translate(' + whiteKeyWidth * (i - 1) + ', 0)'"
        >
          <rect
            :id="'white-' + g + '-' + i"
            class="white-key"
            x="0"
            y="0"
            :width="whiteKeyWidth"
            :height="whiteKeyHeight"
          />
          <text
            :x="whiteKeyWidth / 2 - 7"
            :y="whiteKeyHeight * 0.8"
            class="tips-text"
          >
            {{ i === 1 ? 'C' + g : '' }}
          </text>
        </g>
        <rect
          v-for="i in blackKeyPositions"
          :id="'black-' + g + '-' + i"
          :key="'b' + i"
          class="black-key"
          :x="blackKeyOffset + whiteKeyWidth * (i - 1)"
          y="0"
          :width="blackKeyWidth"
          :height="blackKeyHeight"
        />
      </g>
    </svg>
  </div>
</template>

<script>
import Tone from 'tone'

export default {
  data() {
    return {
      loaded: false,
      blackKeyWidth: 30,
      blackKeyHeight: 100,
      whiteKeyWidth: 35,
      whiteKeyHeight: 200,
      blackKeyPositions: [1, 2, 4, 5, 6],
      whiteKeyPositions: [1, 2, 3, 4, 5, 6, 7],
      syllableNames: ['', 'do', 're', 'mi', 'fa', 'sol', 'la', 'si'],
      pitchNames: ['', 'C', 'D', 'E', 'F', 'G', 'A', 'B'],
      piano: null,
      isMousedown: false
    }
  },
  computed: {
    blackKeyOffset() {
      return (this.whiteKeyWidth * 2 - this.blackKeyWidth) / 2
    }
  },
  mounted() {
    const loadCover = this.$loading({ fullscreen: true })
    // 钢琴
    this.piano = new Tone.Sampler(
      {
        A0: 'A0.mp3',
        C1: 'C1.mp3',
        'D#1': 'Ds1.mp3',
        'F#1': 'Fs1.mp3',
        A1: 'A1.mp3',
        C2: 'C2.mp3',
        'D#2': 'Ds2.mp3',
        'F#2': 'Fs2.mp3',
        A2: 'A2.mp3',
        C3: 'C3.mp3',
        'D#3': 'Ds3.mp3',
        'F#3': 'Fs3.mp3',
        A3: 'A3.mp3',
        C4: 'C4.mp3',
        'D#4': 'Ds4.mp3',
        'F#4': 'Fs4.mp3',
        A4: 'A4.mp3',
        C5: 'C5.mp3',
        'D#5': 'Ds5.mp3',
        'F#5': 'Fs5.mp3',
        A5: 'A5.mp3',
        C6: 'C6.mp3',
        'D#6': 'Ds6.mp3',
        'F#6': 'Fs6.mp3',
        A6: 'A6.mp3',
        C7: 'C7.mp3',
        'D#7': 'Ds7.mp3',
        'F#7': 'Fs7.mp3',
        A7: 'A7.mp3',
        C8: 'C8.mp3'
      },
      {
        release: 0.1,
        baseUrl: '../../../piano/'
      }
    ).toMaster()

    const interval = setInterval(() => {
      if (this.piano.loaded) {
        clearInterval(interval)
        this.loaded = true
        loadCover.close()
      }
    }, 500)

    this.registerKeyboardEvent()
  },
  methods: {
    keyAreaMousedown() {
      this.isMousedown = true
    },
    keyAreaMouseup() {
      this.isMousedown = false
    },
    attack(key, g, i) {
      const sharp = key === 'black' ? '#' : ''
      const note = this.pitchNames[i] + sharp + g
      this.piano.triggerAttack(note)
    },
    release(key, g, i) {
      const sharp = key === 'black' ? '#' : ''
      const note = this.pitchNames[i] + sharp + g
      this.piano.triggerRelease(note, '+4n')
    },
    touch(key, g, i) {
      if (!this.isMousedown) {
        return
      }

      const sharp = key === 'black' ? '#' : ''
      const note = this.pitchNames[i] + sharp + g
      this.piano.triggerAttack(note)
    },
    mouseEvent(e) {
      const keyInfo = e.target.id.split('-')
      if (keyInfo.length !== 3) return

      switch (e.type) {
        case 'mousedown':
          this.attack(keyInfo[0], keyInfo[1], keyInfo[2])
          break
        case 'mouseup':
          this.release(keyInfo[0], keyInfo[1], keyInfo[2])
          break
        case 'mouseout':
          this.release(keyInfo[0], keyInfo[1], keyInfo[2])
          break
        case 'mouseover':
          this.touch(keyInfo[0], keyInfo[1], keyInfo[2])
          break
        default:
      }
    },
    registerKeyboardEvent() {
      const g = 5
      const _this = this
      // 代表Q,W,E,R,T,Y,U
      const keyMapping = [81, 87, 69, 82, 84, 89, 85]

      const getIndex = (event) => {
        const e = event || window.e
        const keyCode = e.keyCode || e.which

        return keyMapping.indexOf(keyCode)
      }

      const setStyleById = (id, className) => {
        const ele = document.getElementById(id)

        ele.setAttribute('class', className)
      }

      document.onkeydown = (event) => {
        const index = getIndex(event)

        if (index === -1) return

        _this.attack('white', g, index + 1)

        setStyleById('white-' + g + '-' + (index + 1), 'pressed')
      }

      document.onkeyup = (event) => {
        const index = getIndex(event)

        if (index === -1) return

        _this.release('white', g, index + 1)

        setStyleById('white-' + g + '-' + (index + 1), 'white-key')
      }
    }
  }
}
</script>

<style scoped>
.piano-bg {
  background-color: #eeeeee;
  padding: 0 5%;
  overflow-x: auto;
}
.white-key {
  fill: white;
  stroke: black;
  stroke-width: 2;
  fill-opacity: 1;
  stroke-opacity: 1;
}
.white-key:hover,
.pressed {
  fill: lightgray;
  stroke: black;
  stroke-width: 1;
}
.black-key {
  fill: #333;
  stroke: black;
  stroke-width: 2;
  fill-opacity: 1;
  stroke-opacity: 1;
}
.black-key:hover {
  fill: #555;
  stroke: black;
  stroke-width: 1;
}
.tips-text {
  user-select: none;
}
@media screen and (max-width: 560px) {
  .piano-bg {
    width: 1786px;
    height: 400px;
    margin-left: -733px;
    margin-top: 693px;
    transform: rotate(90deg);
  }
}
</style>
